use std::collections::HashMap;
use std::path::Path;
use anyhow::{Result, anyhow};
use serde::{Deserialize, Serialize};
use tracing::{debug, error};
use regex::Regex;
use std::{fmt::Debug, vec};
use std::{
    fs::File,
    io::{BufRead, BufReader},
    ops::Range,
};

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct grep_result {
      pub line_prefix: String,
      pub line_end: String,
      pub line_no: usize,
      pub mit_word: String,
}

pub fn grep_file(files: &Vec<String>, grep: &str) -> Result< HashMap<String, Vec<grep_result>> > {        
    let mut ret:HashMap<String, Vec<grep_result>> = HashMap::new();
    let regex = Regex::new(grep).map_err(|_| anyhow!("error to regex"))?;
    // let escapes = &vec!["target".to_string()];
    for file in files {
        let one = file_regex(file, &regex)?;                
        if one.len() > 0 {        
            ret.insert(file.to_string(), one);
        }else{
            debug!("grep {} not found in {}", grep, file)
        } 
    }
   return Ok(ret);
}

/**
 目录遍历，目录检索，目录文件读取

 读取特定目录下符合目标ftypes的所有文件，并放弃指定的特殊目录escapes。

 */
pub fn path_walk(path: &Path, escapes: &Vec<String>, ftypes: &Vec<String>) -> Result<Vec<String>> {
    //let escapes = vec!["target","pkg","__pycache__"];
    //let ftypes = vec!["txt","rs","md","go"];, if ftypes.len()==0, then all file is ok
    // debug!(?path, ?escapes,? ftypes);
    let mut rets: Vec<String> = vec![];
    for entry in path.read_dir()? {
        if let Ok(entry) = entry {
            if let Ok(ft) = entry.file_type() {
                // .unwrap().is_dir(){
                let ename = entry
                    .file_name()
                    .as_os_str()
                    .to_str()
                    .ok_or(anyhow!("e"))?
                    .to_string();
                if ft.is_dir() {
                    // println!("inner -> {:#?}", ename);
                    if !escapes.contains(&ename) && !ename.starts_with(".") {
                        let mut subf = path_walk(&entry.path().as_path(), escapes, ftypes)?;
                        rets.append(&mut subf);
                    } else {
                        // println!("break {:#?}", ename );
                    }
                } else if ft.is_file() {                 
                    let ext = entry
                        .path()
                        .extension()
                        .unwrap_or_default()
                        .to_str()
                        .unwrap_or_default()
                        .to_lowercase();                    
                    // debug!("path = {:#?}, file_name={:#?}, ext = {}", entry.path(), entry.file_name(), ext);
                    if !ename.starts_with(".") && !ename.is_empty() {                        
                        if ftypes.len() == 0 || ftypes.contains(&ext) {
                            rets.push(entry.path().display().to_string());
                        }
                    }
                }
            }
        }
    }
    Ok(rets)
}

pub fn collect_files(todo_dir:&str, ftype:&str) -> Option<Vec<String>> {

    let mypath = Path::new(todo_dir); 
    let p = mypath.parent();
    debug!("parent path is  {:?}", p);

    let escapes = vec![
        "target".to_string(),
        "pkg".to_string(),
        "__pycache__".to_string(),
    ];
  
    // let ftype = "txt, rs, js, py, md, go";
    let ftypes: Vec<String> = ftype
        .split(",")
        .map(|s| s.trim().to_string())
        .collect();
    
    match path_walk(mypath, &escapes, &ftypes) {
        Ok(files) => {
            if files.len() > 0 {
                Some(files)
            } else {
                None
            }
        }
        Err(err) => {
            error!(?err);
            None
        }
    }
}

/// 对文件进行诸行regex查找，并返回命中的行结果。
/// 
pub fn file_regex(filename: &str, regex: &Regex) -> Result< Vec<grep_result> > {
    let f = File::open(&filename).map_err(|_| anyhow!("e"))?;
    let reader = BufReader::new(f);
    //let mut matches = String::new();
    // let mut lines = String::new();
    let mit: Vec<_> = reader
        .lines()
        .enumerate()
        .map(|(lineno, line)| {
            line.ok()
                .map(|line| {
                    regex
                        .find(&line)
                        //.map(|m| format_line_html(&line, lineno + 1, m.range()))
                        .map(|m| format_line(filename, &line, lineno + 1, m.range()))
                })
                .flatten()
        }).filter_map(|v| v.ok_or(()).ok()).collect();
    Ok(mit)
}

/// 格式化输出匹配的行，包含行号、列号和带有高亮的第一个匹配项
pub fn format_line(filename:&str, line: &str, lineno: usize, range: Range<usize>) -> grep_result {
    let Range { start, end } = range;
    let prefix = &line[..start];
    //let color_word = format!("{}", &line[start..end].green().bold())
    /* 
    let line_code = format!(
        "{}<MARK>{}</MARK>{}",        
        prefix.trim_start(),
        &line[start..end],
        &line[end..].trim_end()
    );
    */
    return grep_result{       
        line_prefix: prefix.trim_start().to_owned(),
        line_end: (&line[end..]).trim_end().to_owned(),
        mit_word: (&line[start..end]).to_owned(),
        line_no: lineno,        
    };
}